﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
//using System.Collections.Generic;
using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
using EpiNEXT.Core.Collections;
using EpiNEXT.Core.Fields;
using EpiNEXT.Core.Validation.Actions;

namespace EpiNEXT.Core.Validation
{
    [Serializable()]
    public class Behavior
    {
        /// <summary>
        /// A list of expressions that comprise this condition
        /// </summary>
        [XmlArray("Conditions")]
        [XmlArrayItem("SingleCondition", typeof(SingleCondition))]
        [XmlArrayItem("MultiCondition", typeof(MultiCondition))]
        public ConditionCollection Conditions { get; set; }

        /// <summary>
        /// A list of actions to be carried out when the condition(s) for this behavior are met
        /// </summary>
        [XmlArray("Actions")]
        [XmlArrayItem("Invalidate", typeof(Action_Invalidate))]
        [XmlArrayItem("Disable", typeof(Action_Disable))]
        [XmlArrayItem("Enable", typeof(Action_Enable))]
        [XmlArrayItem("Clear", typeof(Action_Clear))]
        [XmlArrayItem("Assign", typeof(Action_Assign))]
        public ActionCollection Actions { get; set; }

        /// <summary>
        /// A list of actions to be carried out when the condition(s) for this behavior are unmet
        /// </summary>
        [XmlArray("ElseActions")]
        [XmlArrayItem("Invalidate", typeof(Action_Invalidate))]
        [XmlArrayItem("Disable", typeof(Action_Disable))]
        [XmlArrayItem("Enable", typeof(Action_Enable))]
        [XmlArrayItem("Clear", typeof(Action_Clear))]
        [XmlArrayItem("Assign", typeof(Action_Assign))]
        public ActionCollection ElseActions { get; set; }

        /// <summary>
        /// An optional list of locations in which to execute the behavior
        /// </summary>
        [XmlArray("Locations")]
        [XmlArrayItem("Location", typeof(string))]
        public Collection<string> ExecuteLocations { get; set; }

        /// <summary>
        /// Gets whether or not this behavior has specific locations in which it should be executed
        /// </summary>
        public bool HasExecuteLocations
        {
            get
            {
                if (ExecuteLocations != null && ExecuteLocations.Count > 0)
                {
                    return true;
                }
                return false;
            }
        }

        public Behavior()
            : base()
        {
            ExecuteLocations = new Collection<string>();
        }

        public List<IField> GetFieldsInExecuteLocations(DataEntryInstrument form)
        {
            List<IField> fields = new List<IField>();

            foreach (string s in this.ExecuteLocations)
            {
                if (form.Fields.Contains(s))
                {
                    fields.Add(form.Fields[s]);
                }
            }

            return fields;
        }

        public List<IField> GetFieldsInCondition(DataEntryInstrument form)
        {
            List<IField> fields = new List<IField>();

            foreach (ICondition c in this.Conditions)
            {
                fields.AddRange(c.GetFieldsInCondition(form));
            }

            return fields;
        }

        public List<IField> GetFieldsInActions(DataEntryInstrument form)
        {
            List<IField> fields = new List<IField>();

            foreach (IAction a in this.Actions)
            {
                fields.AddRange(a.GetFieldsInAction(form));
            }

            return fields;
        }

        public List<IField> GetFieldsInBehavior(DataEntryInstrument form)
        {
            List<IField> fields = new List<IField>();

            fields.AddRange(GetFieldsInActions(form));
            fields.AddRange(GetFieldsInCondition(form));

            return fields;
        }

        public bool EvaluateCondition(Record record)
        {
            bool isValid = false;

            if (Conditions == null || Conditions.Count == 0)
            {
                // No expressions; there is therefore no condition
                return false;
            }
            else if (Conditions.Count == 1)
            {
                // One expression; evaluate it
                ICondition condition = Conditions.AsEnumerable<ICondition>().First();
                return condition.Evaluate(record);
            }
            else if (Conditions.Count == 2)
            {
                // Two expressions; evaluate both using the supplied operator
                ICondition condition1 = Conditions.AsEnumerable<ICondition>().First();
                ICondition condition2 = Conditions.AsEnumerable<ICondition>().Last();

                bool valid1 = condition1.Evaluate(record);
                bool valid2 = condition2.Evaluate(record);

                switch (Conditions.LogicalOperator)
                {
                    case LogicalOperators.AND:
                        return (valid1 && valid2);
                    case LogicalOperators.OR:
                        return (valid1 || valid2);
                    case LogicalOperators.XOR:
                        return (valid1 ^ valid2);
                }
            }
            else if (Conditions.Count > 2)
            {
                int i = 0;
                // More than two expressions; evaluate all using the supplied operator
                foreach (ICondition condition in Conditions)
                {
                    bool currentConditionValidation = condition.Evaluate(record);

                    if (i == 0)
                    {
                        isValid = currentConditionValidation;
                    }

                    if (i > 0)
                    {
                        switch (Conditions.LogicalOperator)
                        {
                            case LogicalOperators.AND:
                                isValid = (isValid && currentConditionValidation);
                                break;
                            case LogicalOperators.OR:
                                isValid = (isValid || currentConditionValidation);
                                break;
                            case LogicalOperators.XOR:
                                throw new NotImplementedException("XOR not supported for more than two conditions");
                        }
                    }

                    i++;
                }
            }

            return isValid;
        }
    }
}
